/*
Copyright (C) Ariff Ehsan, Abdul Hadi Fikri

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

/*  Author : Ariff Ehsan
 *  Date   : June 23, 2009
 */
 
#include <system.h>

/**
*CODE FUNCTIONALITY
*
*--Function name called by user of this module is in section "Provides", "Uses" is for any function 
*--name this module need to call in turn to signaled an event for the user.
*
*Interface module_name{
*	Provides{
*		command void PRINTF_init(void)
*		command int uart_putchar(char c, FILE *stream) @ printf();
*	}
*	Uses{
*	}
*}
*
*--Component in call section are function name which this module use from other module in order to complete its
*--tasks, event in turn is any event siganalled by the other module(s).  
*
*Component{
*	call{
*		SERIAL0->SERIAL0_reg(x,y,z)
*		SERIAL0->SERIAl0_init()
*		TASK   ->TASK_reg(x,y)
*		TASK   ->TASK_post(x)
*	}
*	event{
*		PRINTF_TASK_PUTCHAR_run()<- TASK
*		PRINTF_SERIAL_sendDone() <- SERIAL0
*	}
*}
*
**
*Data Field:
*/
  
  task_t printf_task_putchar;

  uint8_t printf_buffer[PRINTF_BUFFER_SIZE];
  uint8_t printf_in, printf_out;

  BOOL printf_busy, printf_full;

/**
*Function Field:
*/void PRINTF_TASK_PUTCHAR_run(void);

  static int uart_putchar(char c, FILE *stream);
//static FILE atm128_stdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

  void PRINTF_SERIAL_sendDone(void);

/********************************************************************************************/
/*                                  Clients/Command Call                                    */
/********************************************************************************************/
void PRINTF_init(void) {

	/* register task */
	TASK_reg(&printf_task_putchar, PRINTF_TASK_PUTCHAR_run);
	
	/* initialize printf variables */
	printf_in = printf_out = 0;
	printf_busy = printf_full = FALSE;
	
	/* wire putchar function */
	fdevopen(&uart_putchar,NULL);
	
	/* initialize and register serial driver */
	DRIVER_SERIAL_INIT();
	DRIVER_SERIAL_REG(PRINTF_SERIAL_sendDone, NULL, NULL);
}

int uart_putchar(char c, FILE *stream) {
	//ENTER_CRITICAL();
	
	/* check if queuing if already full */
	if (printf_full) {
		//EXIT_CRITICAL();
		return -1;
	}
	
	/* insert into queue and update */
	printf_buffer[printf_in] = c;
	printf_in = (printf_in + 1) % PRINTF_BUFFER_SIZE;
	
	if (printf_in == printf_out)
		printf_full = TRUE;
	
	if (!printf_busy) {
		printf_busy = TRUE;
		//EXIT_CRITICAL();
		
		TASK_post(&printf_task_putchar);
	}
	//else
	//	EXIT_CRITICAL();
	
	return 0;
}
/********************************************************************************************/
/*                                 Internal Function Call                                   */
/********************************************************************************************/

/********************************************************************************************/
/*                              HW/SW Interrupt/Event Signalled                             */
/********************************************************************************************/
void PRINTF_TASK_PUTCHAR_run(void) {
	
	//ENTER_CRITICAL();
	
	if (printf_in == printf_out && !printf_full) {
		printf_busy = FALSE;
		//EXIT_CRITICAL();
		return;
	}
	
	uint8_t data = printf_buffer[printf_out];
	
	//EXIT_CRITICAL();
	
	DRIVER_SERIAL_SEND(data);
}

void PRINTF_SERIAL_sendDone(void) {

	//ENTER_CRITICAL();
	
	/* update output pointer */
	printf_out = (printf_out + 1) % PRINTF_BUFFER_SIZE;
	printf_full = FALSE;
	
	//EXIT_CRITICAL();
	
	/* repost task */
	TASK_post(&printf_task_putchar);
	
}